/**
 * Copyright 2006-2015 the original author or authors.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.mybatis.generator;

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.CommentGeneratorConfiguration;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.Context;
import org.mybatis.generator.config.GeneratedKey;
import org.mybatis.generator.config.JDBCConnectionConfiguration;
import org.mybatis.generator.config.JavaClientGeneratorConfiguration;
import org.mybatis.generator.config.JavaModelGeneratorConfiguration;
import org.mybatis.generator.config.ModelType;
import org.mybatis.generator.config.PropertyRegistry;
import org.mybatis.generator.config.SqlMapGeneratorConfiguration;
import org.mybatis.generator.config.TableConfiguration;
import org.mybatis.generator.configuration.MyBatisGenertorProperties;
import org.mybatis.generator.exception.InvalidConfigurationException;
import org.mybatis.generator.internal.DefaultShellCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class MyBatisGeneratorBuilder {

    private static final Logger LOG = LoggerFactory.getLogger(MyBatisGeneratorBuilder.class);

    private DataSource dataSource;

    private JDBCConnectionConfiguration jdbcConnectionConfiguration;

    private MyBatisGenertorProperties properties;

    public MyBatisGeneratorBuilder() {
        properties = new MyBatisGenertorProperties();
    }

    /**
     * 设置数据源
     *
     * @param dataSource 数据源
     * @return
     * @auther ebert_chan
     */
    public MyBatisGeneratorBuilder jdbc(DataSource dataSource) {
        this.dataSource = dataSource;
        return this;
    }

    /**
     * 设置数据源
     *
     * @param jdbcConnectionConfiguration 数据库连接配置
     * @return
     * @auther ebert_chan
     */
    public MyBatisGeneratorBuilder jdbc(JDBCConnectionConfiguration jdbcConnectionConfiguration) {
        this.jdbcConnectionConfiguration = jdbcConnectionConfiguration;
        jdbcConnectionConfiguration.addProperty("nullCatalogMeansCurrent", "true");
        return this;
    }

    /**
     * 设置源码包名
     *
     * @param targetPackage 包名，例如：org.tianyun.cloud
     * @return
     * @auther ebert_chan
     */
    public MyBatisGeneratorBuilder targetPackage(String targetPackage) {
        properties.setTargetPackage(targetPackage);
        return this;
    }

    /**
     * 数据库表自增字段
     *
     * @param generatedKey 指定数据库自增字段，默认：id
     * @return
     * @auther ebert_chan
     */
    public MyBatisGeneratorBuilder generatedKey(String generatedKey) {
        properties.setGeneratedKey(generatedKey);
        return this;
    }

    /**
     * 要生成源码的表名，%为所有表
     *
     * @param tableNames 指定表名，多个以,隔开，默认%为所有表生成源码
     * @return
     * @auther ebert_chan
     */
    public MyBatisGeneratorBuilder tableNames(String tableNames) {
        properties.setTableNames(tableNames);
        return this;
    }

    /**
     * 设置源码Domain继承自RootClass
     *
     * @param rootClass 父类
     * @return
     * @auther ebert_chan
     */
    public MyBatisGeneratorBuilder domainRootClass(Class<?> rootClass) {
        if (rootClass != null) {
            properties.setDomainRootClass(rootClass.getName());
        }
        return this;
    }

    /**
     * 设置源码Domain继承自RootClass
     *
     * @param rootClass 父类
     * @return
     * @auther ebert_chan
     */
    public MyBatisGeneratorBuilder domainRootClass(String rootClass) {
        if (rootClass != null) {
            properties.setDomainRootClass(rootClass);
        }
        return this;
    }

    /**
     * 生成源码
     *
     * @throws InvalidConfigurationException
     * @throws SQLException
     * @throws IOException
     * @throws InterruptedException
     * @auther ebert_chan
     */
    public void genertor() throws InvalidConfigurationException, SQLException, IOException, InterruptedException {
        Context context = new Context(ModelType.FLAT);
        context.setId("MyBatis3");
        context.setTargetRuntime("MyBatis3");
        context.setDataSource(dataSource);
        context.setJdbcConnectionConfiguration(jdbcConnectionConfiguration);

        JavaModelGeneratorConfiguration javaModelGeneratorConfiguration = new JavaModelGeneratorConfiguration();
        javaModelGeneratorConfiguration.setTargetPackage(properties.getTargetPackage());
        javaModelGeneratorConfiguration.setTargetProject("target/generated-sources/mybatis3/src/main/java");
        javaModelGeneratorConfiguration.addProperty(PropertyRegistry.MODEL_GENERATOR_TRIM_STRINGS, "true");
        context.setJavaModelGeneratorConfiguration(javaModelGeneratorConfiguration);

        SqlMapGeneratorConfiguration sqlMapGeneratorConfiguration = new SqlMapGeneratorConfiguration();
        sqlMapGeneratorConfiguration.setTargetPackage("mapper");
        sqlMapGeneratorConfiguration.setTargetProject("target/generated-sources/mybatis3/src/main/resources");
        context.setSqlMapGeneratorConfiguration(sqlMapGeneratorConfiguration);

        JavaClientGeneratorConfiguration javaClientGeneratorConfiguration = new JavaClientGeneratorConfiguration();
        javaClientGeneratorConfiguration.setTargetPackage(properties.getTargetPackage());
        javaClientGeneratorConfiguration.setTargetProject("target/generated-sources/mybatis3/src/main/java");
        javaClientGeneratorConfiguration.setConfigurationType("XMLMAPPER");
        context.setJavaClientGeneratorConfiguration(javaClientGeneratorConfiguration);

        if ("%".equals(properties.getTableNames())) {
            this.buildTableConfiguration(context, properties, properties.getTableNames());
        } else {
            String[] array = properties.getTableNames().replaceAll("，", ",").split(",");
            for (int i = 0; i < array.length; i++) {
                this.buildTableConfiguration(context, properties, array[i].trim());
            }
        }

        Configuration config = new Configuration();
        config.addContext(context);

        CommentGeneratorConfiguration commentGeneratorConfiguration = new CommentGeneratorConfiguration();
        commentGeneratorConfiguration.addProperty(PropertyRegistry.COMMENT_GENERATOR_ADD_REMARK_COMMENTS, "true");
        commentGeneratorConfiguration.addProperty(PropertyRegistry.COMMENT_GENERATOR_SUPPRESS_DATE, "true");
        context.setCommentGeneratorConfiguration(commentGeneratorConfiguration);

        DefaultShellCallback shellCallback = new DefaultShellCallback(true);
        List<String> warnings = new ArrayList<String>();
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, shellCallback, warnings);
        myBatisGenerator.generate();

        if (warnings.isEmpty()) {
            return;
        }

        for (String msg : warnings) {
            LOG.warn(msg);
        }

    }

    private void buildTableConfiguration(Context context, MyBatisGenertorProperties properties, String tableName) {
        TableConfiguration tc = new TableConfiguration(context);
        tc.setTableName(tableName);
        tc.setAlias("%".equals(tableName) ? "t" : String.valueOf(tableName.charAt(0)));
        tc.setSelectByExampleStatementEnabled(false);
        tc.setCountByExampleStatementEnabled(false);
        tc.setDeleteByExampleStatementEnabled(false);
        tc.setUpdateByExampleStatementEnabled(false);
        tc.setInsertStatementEnabled(true);
        tc.setDeleteByPrimaryKeyStatementEnabled(true);
        tc.setUpdateByPrimaryKeyStatementEnabled(true);
        tc.setSelectByPrimaryKeyStatementEnabled(true);
        if (properties.getDomainRootClass() != null && !properties.getDomainRootClass().equals("")) {
            tc.addProperty(PropertyRegistry.ANY_ROOT_CLASS, properties.getDomainRootClass());
        }
        GeneratedKey generatedKey = new GeneratedKey(properties.getGeneratedKey(), "JDBC", false, null);
        tc.setGeneratedKey(generatedKey);
        context.addTableConfiguration(tc);
    }

}
